package com.mwj.generator.config;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
 * @author mengweijin
 */
@Slf4j
public class CodeGenerator {

    public void generator() {
        ConfigProperty config = ConfigProperty.getInstance();
        // 代码生成器
        AutoGenerator autoGenerator = new AutoGenerator();

        // 全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(config.getOutputPath());
        globalConfig.setAuthor(config.getAuthor());
        globalConfig.setOpen(false);
        globalConfig.setFileOverride(true);
        autoGenerator.setGlobalConfig(globalConfig);

        // 数据源配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl(config.getDbUrl());
        dataSourceConfig.setDriverName(config.getDbDriverName());
        dataSourceConfig.setUsername(config.getDbUsername());
        dataSourceConfig.setPassword(config.getDbPassword());
        autoGenerator.setDataSource(dataSourceConfig);

        // 包配置
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setParent(config.getPackageParent());
        packageConfig.setModuleName(config.getModuleName());
        packageConfig.setEntity(null);
        packageConfig.setService(null);
        packageConfig.setServiceImpl(null);
        packageConfig.setMapper(null);
        packageConfig.setXml(null);
        packageConfig.setController(null);
        packageConfig.setPathInfo(null);
        autoGenerator.setPackageInfo(packageConfig);

        // 自定义配置, 会被优先输出
        InjectionConfig injectionConfig = injectionConfig(config);
        injectionConfig.setFileOutConfigList(loadTemplates(config));
        autoGenerator.setCfg(injectionConfig);

        // 模板配置, 不想生成的就设为null
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setEntity(null);
        templateConfig.setEntityKt(null);
        templateConfig.setService(null);
        templateConfig.setServiceImpl(null);
        templateConfig.setMapper(null);
        templateConfig.setXml(null);
        templateConfig.setController(null);
        autoGenerator.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setSuperEntityClass(config.getSuperEntityClass());
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // 公共父类
        //strategy.setSuperControllerClass(BASE_CONTROLLER_CLASS);
        // 写于父类中的公共字段
        strategy.setSuperEntityColumns(config.getSuperEntityColumns());
        strategy.setInclude(config.getTables());
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(config.getTablePrefix());
        autoGenerator.setStrategy(strategy);

        // 模板引擎
        autoGenerator.setTemplateEngine(config.getTemplateEngine());

        autoGenerator.execute();
    }

    private InjectionConfig injectionConfig(ConfigProperty config) {
        InjectionConfig injectionConfig = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
            @Override
            public Map<String, Object> prepareObjectMap(Map<String, Object> objectMap) {
                Map<String, Object> map = new HashMap<>(6);
                map.put("mwj_author", objectMap.get("author"));
                map.put("mwj_date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                map.put("mwj_table", objectMap.get("table"));
                map.put("mwj_entity", objectMap.get("entity"));
                map.put("mwj_entityVariableName", StrUtil.lowerFirst(String.valueOf(objectMap.get("entity"))));
                map.put("mwj_superEntityClass", objectMap.get("superEntityClass"));
                map.put("mwj_package", config.getPackageParent() + (StrUtil.isBlank(config.getModuleName()) ? "" : StringPool.DOT + config.getModuleName()));
                map.put("mwj_config", config);
                map.put("mwj_fieldImportPackages", handleFieldImportPackage((TableInfo) objectMap.get("table")));
                map.put("mwj_idPropertyType", handleIdPropertyType((TableInfo) objectMap.get("table")));
                map.put("mwj_idField", handleIdField((TableInfo) objectMap.get("table")));
                map.put("mwj_AllFieldList", handleAllFieldList((TableInfo) objectMap.get("table")));

                objectMap.putAll(map);
                return objectMap;
            }
        };

        return injectionConfig;
    }

    private List<FileOutConfig> loadTemplates(ConfigProperty config) {
        // 自定义输出配置
        List<FileOutConfig> fileOutConfigList = new ArrayList<>();

        String templateSuffix = config.getTemplateSuffix();
        List<File> fileList = FileUtil.loopFiles("templates", file -> file.getName().toLowerCase().endsWith(templateSuffix));
        fileList.forEach(file -> {
            if(file.isFile()) {
                // 自定义配置会被优先输出
                String filePath = file.getAbsolutePath();
                FileOutConfig fileOutConfig = new FileOutConfig(filePath.substring(filePath.indexOf(File.separator + "templates" + File.separator))) {
                    @Override
                    public String outputFile(TableInfo tableInfo) {
                        StringBuilder outputPath = new StringBuilder();
                        outputPath.append(config.getOutputPath()).append(File.separator);
                        outputPath.append(file.getParentFile().getName()).append(File.separator);
                        outputPath.append(config.getPackageParent().replace(StringPool.DOT, File.separator)).append(File.separator);
                        outputPath.append(config.getModuleName()).append(File.separator);
                        StringBuilder componentName = new StringBuilder();
                        String[] packageHierarchy = file.getName().split("\\.");
                        if("entity".equalsIgnoreCase(packageHierarchy[0])) {
                            outputPath.append(packageHierarchy[0]).append(File.separator);
                        } else {
                            for(int i = 0; i < packageHierarchy.length - 2; i++) {
                                componentName.append(NamingStrategy.capitalFirst(packageHierarchy[i]));
                                outputPath.append(packageHierarchy[i].toLowerCase()).append(File.separator);
                            }
                        }

                        outputPath.append(tableInfo.getEntityName()).append(componentName).append(StringPool.DOT + packageHierarchy[packageHierarchy.length - 2]);

                        // 自定义输出文件名，如果你 Entity 设置了前后缀、此处的名称会跟着发生变化！！
                        return outputPath.toString();
                    }
                };
                fileOutConfigList.add(fileOutConfig);
            }
        });

        return fileOutConfigList;
    }

    private Set<String> handleFieldImportPackage(TableInfo tableInfo) {
        Set<String> set = new HashSet<>();
        tableInfo.getFields().forEach(tableField -> {
            IColumnType columnType = tableField.getColumnType();
            if(columnType.getPkg() != null){
                set.add(columnType.getPkg());
            }
        });

        return set;
    }

    private String handleIdPropertyType(TableInfo tableInfo) {
        Optional<String> optional = tableInfo.getFields().stream()
                .filter(TableField::isKeyFlag)
                .map(tableField -> tableField.getColumnType().getType()).findFirst();
        return optional.orElse(DbColumnType.LONG.getType());
    }

    private TableField handleIdField(TableInfo tableInfo) {
        Optional<TableField> optional = tableInfo.getFields().stream().filter(TableField::isKeyFlag).findFirst();
        return optional.orElse(new TableField());
    }

    private List<TableField> handleAllFieldList(TableInfo tableInfo) {
        List<TableField> fields = tableInfo.getFields();
        List<TableField> commonFields = tableInfo.getCommonFields();
        List<TableField> allFields = new ArrayList<>();
        allFields.addAll(fields);
        allFields.addAll(commonFields);
        return allFields;
    }

}
